#include "StdAfx.h"
#include "ThreadSyncer.h"

ThreadSyncer::ThreadSyncer(void)
{
	//TRACE(_T("-> thread.create: %d\n"), (int)this);

	mbufnum = 2;
	musernum = 2;

	mmutex.mutex = NULL;
	mmutex.num = 0;
	muser.user = NULL;
	muser.num = 0;
}

ThreadSyncer::~ThreadSyncer(void)
{
	//TRACE(TEXT("-- threadsyncer.destory: %d \n"), (int)this);

}
int ThreadSyncer::init()
{
	createMutex(&mmutex, mbufnum);
	createUser(&muser,musernum, mbufnum);
	return 0;
}
void ThreadSyncer::exit()
{
	deleteMutex(&mmutex);
	deleteUser(&muser);
}
/*
int ThreadSyncer::Init(int bufnum, int usernum )
{
	mbufnum = bufnum;
	musernum = usernum;

	createMutex(&mmutex, mbufnum);
	createUser(&muser,musernum, mbufnum);

	return 1;
}
*/
int ThreadSyncer::get( int id )
{
	int ret = 0;

	syuser_t * user = &muser.user[id];
	int bufnum = user->getnum;

	int t = ::WaitForSingleObject( mmutex.mutex[bufnum], user->waittime );
	switch ( t )
	{
	case WAIT_OBJECT_0:	// successed;
		ret = bufnum; 
		user->islock = true;
		break;
	case WAIT_TIMEOUT:
		ret = -1;
		//		GWarning( TEXT("syncer get: time out."));
		break;
	default:
		//		GWarning( TEXT(" syncer get failed. "));
		ret = -2;
		break;
	}

	return ret;
}
void ThreadSyncer::rls(int id, bool next )
{
	//	VERIFY( id >= 0 || id < musernum );

	// next: if false, then will not inc bufnum, so next time will get the same one;
	// if true, then after release, inc bufnum;
	// default: next=true;

	syuser_t * user = &muser.user[id];
	if( user->islock )
	{
		int num = user->getnum;
		::ReleaseMutex( mmutex.mutex[num] );
		user->islock = false;

		if( next )	// 2012-02-29;// set next getnum;
		{
			incGetnum(user);
			//		if( ++num >= mmutex.num )
			//			num = 0;
			//		user->getnum = num;
		}
	}
}

int ThreadSyncer::Get( int id, int *pnum )
{
	int ret = 0;

//	VERIFY(id >= 0 && id < muser.num );

	//	Release( id, false );

	syuser_t * user = &muser.user[id];
	int bufnum = user->getnum;

	int t = ::WaitForSingleObject( mmutex.mutex[bufnum], user->waittime );
	switch ( t )
	{
	case WAIT_OBJECT_0:	// successed;
		*pnum = bufnum; 
		user->islock = true;
		ret = 1;
		break;
	case WAIT_TIMEOUT:
		ret = -1;
		//		GWarning( TEXT("syncer get: time out."));
		break;
	default:
		//		GWarning( TEXT(" syncer get failed. "));
		ret = 0;
		break;
	}

	return ret;
}
int	ThreadSyncer::getInc( int id, int *pnum)
{
	int d = this->Get( id, pnum );
	if ( d != 1)
	{
		incGetnum( &muser.user[id] );
	}

	return d;
}
/*
void ThreadSyncer::incGetnum( syuser_t * user )
{
	if ( ++user->getnum >= mmutex.num )
	{
		user->getnum = 0;
	}
}
*/
int ThreadSyncer::Release(int id, bool next )
{
	//	VERIFY( id >= 0 || id < musernum );

	// next: if false, then will not inc bufnum, so next time will get the same one;
	// if true, then after release, inc bufnum;
	// default: next=true;

	syuser_t * user = &muser.user[id];
	if( user->islock )
	{
		int num = user->getnum;
		::ReleaseMutex( mmutex.mutex[num] );
		user->islock = false;

		if( next )	// 2012-02-29;// set next getnum;
		{
			incGetnum(user);
	//		if( ++num >= mmutex.num )
	//			num = 0;
	//		user->getnum = num;
		}
	}
	return 1;
}
// 2013-06-22; these functions use separate user info setting;	
int		ThreadSyncer::Get( int id, int num )
{
	int d = 0;
	//	d = Release( id, false );
	//	if( d < 0 )	return d;

	userinf_t * inf = &muser.user[id].infs[num];
	int t = ::WaitForSingleObject( mmutex.mutex[num], inf->waittime );
	switch ( t )
	{
	case WAIT_OBJECT_0:	// successed;
		inf->islock = true;
		d = 1;
		break;
	case WAIT_TIMEOUT:
		d = -1;
		//glog->warn( TEXT("syncer get: time out."));
		break;
	default:
		//glog->warn( TEXT(" syncer get failed. "));
		d = 0;
		break;
	}
	return d;
}
int		ThreadSyncer::Release( int id, int num, bool next )
{
	userinf_t * inf = &muser.user[id].infs[num];
	if( inf->islock )
	{
		::ReleaseMutex( mmutex.mutex[num] );
		inf->islock = false;
	}
	return 1;
}

void	ThreadSyncer::Reset( int id )
{
	VERIFY( muser.num > id);

	muser.user[id].getnum = 0;
}
void	ThreadSyncer::SetWaittime(int user, int tm, int num )
{
	VERIFY( muser.num > user );
	VERIFY( muser.user[user].ifnum >= num);

	muser.user[user].infs[num].waittime = tm;
}
void ThreadSyncer::setWaitTime(int user, int tm)
{
	VERIFY( muser.num > user );

	muser.user[user].waittime = tm;

	// for indivadal set; not used yet;
	for (int i =0; i <muser.user[user].ifnum; i++)
	{
		muser.user[user].infs[i].waittime = tm;
	}
}
void ThreadSyncer::createMutex(mutex_t * mutex, int num)
{
	VERIFY( mutex && num>0);

	mutex->num = num;
	mutex->mutex = new HANDLE[ num ]; //TRACE(_T("=== mutex->mutex: 0x%x\n"), mutex->mutex);
	gptAddress(mutex->mutex, TEXT("ThreadSyncer::createMutex.mutex->mutex"), sizeof(HANDLE)*num);
	for( int i = 0; i < num; i++)
		mutex->mutex[i] = ::CreateMutex( NULL, FALSE, NULL );
}
void ThreadSyncer::deleteMutex(mutex_t * mutex)
{
	VERIFY(mutex);
	for (int i = 0; i < mutex->num; i++)
	{
		::CloseHandle( mutex->mutex[i] );
	}
	if (mutex->mutex)
	{
		delete [] mutex->mutex;
		mutex->mutex = 0;
		mutex->num = 0;
	}
}
void ThreadSyncer::createUser(user_t* user, int num, int bufnum)
{
	VERIFY(user && num >0 && bufnum > 0 );

	user->user = new syuser_t[num]; //TRACE(_T("=== user->user: 0x%x\n"), user->user);
	gptAddress(user->user, TEXT("ThreadSyncer::createUser.user->user"), sizeof(syuser_t)*num);
	user->num = num;
	for(int i=0; i<num; i++)
	{
		user->user[i].getnum = 0;
		user->user[i].islock = false;
		user->user[i].waittime = 10*1000; // 10s;
		
		// 2012-03-06; not used yet;
		user->user[i].infs = new userinf_t[bufnum]; //TRACE(_T("=== user->user[i].infs: 0x%x\n"), user->user[i].infs);
		gptAddress(user->user[i].infs, TEXT("ThreadSyncer::createUser.user->user[i].infs"), sizeof(userinf_t)*bufnum);
		user->user[i].ifnum = bufnum;
		for (int j=0; j < bufnum; j++)
		{
			userinf_t * inf = &user->user[i].infs[j];
			inf->islock = false;
			inf->waittime = 10*1000; // 10s; //INFINITE
		}
	}
}
void ThreadSyncer::deleteUser(user_t * user)
{
	VERIFY(user);
	for (int i = 0; i <user->num; i++)
	{
		if (user->user[i].infs)
		{
			delete [] user->user[i].infs;
			user->user[i].infs = NULL;
			user->user[i].ifnum = 0;
		}
	}
	if (user->user)
	{
		delete [] user->user;
		user->user = NULL;
		user->num = 0;
	}
}

